package com.yxk.bootvueshop.tool;

import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * Created by baipan
 * Date: 2019/11/28
 * DefaultBeanMapper
 */
@Primary
@Component
public class DefaultBeanMapper implements BeanMapper, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    @Override
    public <S extends Serializable, T extends Serializable> T map(S source, T target, boolean coverNull) {
        if (coverNull){
            BeanUtils.copyProperties(source, target);
            return target;
        }
        BeanUtils.copyProperties(source, target, Objects.requireNonNull(getNullProperties(source)));
        return target;
    }


    @Override
    public <S extends Serializable, T extends Serializable> T map(S source, Class<T> target) {
        T t = null;
        try {
            t = target.newInstance();
            if (source != null){
                BeanUtils.copyProperties(source, t);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }


    @Override
    public <S extends Serializable, T extends Serializable> List<T> mapList(Collection<S> source, Class<T> target) {
        if (!CollectionUtils.isEmpty(source)){
            return source.stream()
                    .map(s -> map(s, target))
                    .collect(Collectors.toList());
        }
        return new ArrayList<>();
    }


    @Override
    public <S extends Serializable, T extends Serializable> T mapFunction(S source, Function<S, T> function) {
        return function.apply(source);
    }


    @Override
    public <S extends Collection, T extends Collection, M extends Class> T mapListFunction(S source, M targetClazz, BiFunction<S, M, T> biFunction) {
        if (!CollectionUtils.isEmpty(source)){
            return biFunction.apply(source, targetClazz);
        }
        return null;
    }


    @Override
    public <B> B getBean(Class<B> clazz) {
        return this.applicationContext.getBean(clazz);
    }


    @Override
    public <B> B getBean(String beanName, Class<B> clazz) {
        return this.applicationContext.getBean(beanName, clazz);
    }


    @Override
    public <B extends Annotation> List<Object> getBeanByAnnotation(Class<B> clazz) {
        Map<String, Object> beans = this.applicationContext.getBeansWithAnnotation(clazz);
        if (!CollectionUtils.isEmpty(beans)) {
            return new ArrayList<>(beans.values());
        }
        return new ArrayList<>();
    }


    private String[] getNullProperties(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();
        if (ArrayUtils.isNotEmpty(pds)){
            return Arrays.stream(pds)
                    .filter(p -> {
                        Object srcValue = src.getPropertyValue(p.getName());
                        return Objects.isNull(srcValue);
                    })
                    .map(PropertyDescriptor :: getName)
                    .toArray(String[] :: new);
        }
        return new String[0];
    }


}
